﻿#include "vk.h"

#include <vector>

const uint32_t kWidth = 800;
const uint32_t kHeight = 600;

const std::vector<const char*> kValidationLayers = {
    "VK_LAYER_KHRONOS_validation"
};

#ifdef NDEBUG
const bool kEnableValidationLayers = false;
#else
const bool kEnableValidationLayers = true;
#endif

HelloTriangleApplication::HelloTriangleApplication()
    : win_(nullptr), instance_(nullptr) {}

void HelloTriangleApplication::run() {
  InitVulkan();
  InitWindow();
  MainLoop();
  Cleanup();
}

void HelloTriangleApplication::InitVulkan() { CreateInstance(); }

void HelloTriangleApplication::InitWindow() {
  glfwInit();

  glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
  glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

  win_ = glfwCreateWindow(kWidth, kHeight, "Vulkan", nullptr, nullptr);
}

void HelloTriangleApplication::MainLoop() {
  while (!glfwWindowShouldClose(win_)) {
    glfwPollEvents();
  }
}

void HelloTriangleApplication::Cleanup() {
  vkDestroyInstance(instance_, nullptr);
  glfwDestroyWindow(win_);
  glfwTerminate();
}

void HelloTriangleApplication::CreateInstance() {
  if (kEnableValidationLayers && !CheckValidationLayerSupport()) {
    throw std::runtime_error("validation layers requested, but not available.");
  }

  VkApplicationInfo app_info{};
  app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
  app_info.pApplicationName = "Hello Triangle";
  app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
  app_info.pEngineName = "No Engine";
  app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
  app_info.apiVersion = VK_API_VERSION_1_0;

  VkInstanceCreateInfo create_info{};
  create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
  create_info.pApplicationInfo = &app_info;

  uint32_t glfw_extension_count = 0;
  const char** glfw_extensions;

  glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count);

  create_info.enabledExtensionCount = glfw_extension_count;
  create_info.ppEnabledExtensionNames = glfw_extensions;
  
  if (kEnableValidationLayers) {
    std::cout << "is debug" << std::endl;
    create_info.enabledExtensionCount = static_cast<uint32_t>(kValidationLayers.size());
    create_info.ppEnabledLayerNames = kValidationLayers.data();
  } else {
    create_info.enabledLayerCount = 0;
    create_info.pNext = nullptr;
  }

  VkResult rs = vkCreateInstance(&create_info, nullptr, &instance_);
  if (VK_SUCCESS != rs) {
    std::cerr << "failed to create instance, error code: " << rs << std::endl;
    throw std::runtime_error("failed to create instance!");
  }

  uint32_t extension_count = 0;
  vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);

  std::vector<VkExtensionProperties> extensions(extension_count);
  vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data());
  std::cout << "available extensions:" << std::endl;
  for (const auto& extension : extensions) {
    std::cout << '\t' << extension.extensionName << '\n';
  }
}

bool HelloTriangleApplication::CheckValidationLayerSupport() {
  uint32_t layer_count = 0;
  vkEnumerateInstanceLayerProperties(&layer_count, nullptr);

  std::vector<VkLayerProperties> available_layers(layer_count);
  vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data());

  for (auto layer_name : kValidationLayers) {
    bool layer_found = false;

    for (const auto& layer_properties : available_layers) {
      if (0 == strcmp(layer_name, layer_properties.layerName)) {
        layer_found = true;
        break;
      }
    }

    if (!layer_found) {
      return false;
    }
  }

  return true;
}

int main() {
  HelloTriangleApplication app;

  try {
    app.run();
  } catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;
    return -1;
  }
  return 0;
}
